package com.lemon.boot.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

/**
 * @author 李猛
 * @datetime 2024/3/4 13:10
 * @description 加密解密工具类
 */
@Slf4j
public class AesUtil {
    /**
     * AES算法名称
     */
    private static final String AES_KEY_ALGORITHM = "AES";
    /**
     * AES算法名称
     * /加密模式(ECB：电子密本模式、CBC：密文分组链接模式；CFB：密文反馈模式；OFB：输出反馈模式)
     * /填充方式(NoPadding：不填充，原文字节是8的倍数；PKCS5Padding：填充至符合块大小的整数倍，填充值为填充数量数；ISO10126Padding：填充至符合块大小的整数倍，填充值最后一个字节为填充的数量数，其他字节随机处理)
     */
    private static final String AES_CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";

    /**
     * AES keyIV
     */
    private static final String KEY_IV = "0123456789111213";

    /**
     * 秘钥长度
     */
    private static final int SECRET_KEY_LEN = 16;


    private static AesUtil instance;

    private AesUtil() {
    }

    public static AesUtil getInstance() {
        if (instance == null) {
            synchronized (AesUtil.class) {
                if (instance == null) {
                    instance = new AesUtil();
                }
            }
        }
        return instance;
    }

    /**
     * 加密
     *
     * @param text 待加密数据
     * @param key  加密key
     * @param ivs  iv向量
     * @return java.lang.String 加密后的数据
     */
    public String encrypt(String text, String key, String ivs) {
        if (key == null) {
            return null;
        }
        if (key.length() != SECRET_KEY_LEN) {
            return null;
        }
        // 使用CBC模式，需要一个向量iv，可增加加密算法的强度, 默认使用上面定义好的iv
        if (ivs == null) {
            ivs = KEY_IV;
        }
        try {
            Cipher cipher = Cipher.getInstance(AES_CIPHER_ALGORITHM);
            byte[] raw = key.getBytes();
            SecretKeySpec skeySpec = new SecretKeySpec(raw, AES_KEY_ALGORITHM);

            IvParameterSpec iv = new IvParameterSpec(ivs.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
            byte[] encrypted = cipher.doFinal(text.getBytes(StandardCharsets.UTF_8));
            // 此处使用BASE64做转码。
            return Base64.encodeBase64String(encrypted);
        } catch (Exception e) {
            log.error("AesUtil.encrypt.message:{}", e.getMessage());
            return null;
        }
    }

    /**
     * 加密
     *
     * @param text
     * @param key
     * @return
     */
    public String encrypt(String text, String key) {
        return encrypt(text, key, null);
    }

    /**
     * 解密
     *
     * @param text 待解密数据
     * @param key  解密key
     * @param ivs  解密iv
     * @return java.lang.String 解密后的数据
     */
    public String decrypt(String text, String key, String ivs) {
        if (ivs == null) {
            ivs = KEY_IV;
        }
        try {
            byte[] raw = key.getBytes(StandardCharsets.US_ASCII);
            SecretKeySpec skeySpec = new SecretKeySpec(raw, AES_KEY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(AES_CIPHER_ALGORITHM);

            IvParameterSpec iv = new IvParameterSpec(ivs.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] encrypted1 = Base64.decodeBase64(text);
            byte[] original = cipher.doFinal(encrypted1);
            return new String(original, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("AesUtil.decrypt.message:{}", e.getMessage());
            return null;
        }
    }

    /**
     * 解密
     *
     * @param text 待解密数据
     * @param key  解密key
     * @return
     */
    public String decrypt(String text, String key) {
        return decrypt(text, key, null);
    }

    public static void main(String[] args) {
        String key = "1234567812345678";
        String strEnc = AesUtil.getInstance().encrypt("123456aa", key);
        System.out.println(strEnc);
        String strDec = AesUtil.getInstance().decrypt(strEnc, key);
        System.out.println(strDec);
    }
}

